Dykk ned i JavaScript Temporal Duration, det moderne API-et for presis aritmetikk, sammenligning og formatering av tidsintervaller. Lær å håndtere tidsperioder på en globalt bevisst måte, og unngå vanlige fallgruver med Date-objekter.
JavaScript Temporal Duration: Mestre aritmetikk og formatering av tidsintervaller for globale applikasjoner
Å håndtere tid i programvareutvikling er notorisk komplekst. Fra å spore prosjekttidslinjer på tvers av kontinenter til å planlegge internasjonale videokonferanser, kan nyansene i tidsintervaller, tidssoner og sommertid raskt føre til subtile, men kritiske feil. I tiår har JavaScript-utviklere slitt med det innebygde Date-objektet, et verktøy som, selv om det er funksjonelt for enkle tidspunkter, kommer til kort når det gjelder presis tidsintervallaritmetikk og robust, globalt bevisst tidsstyring.
Her kommer JavaScript Temporal API – et banebrytende forslag designet for å tilby et moderne, robust og brukervennlig API for å arbeide med datoer og tider i JavaScript. Blant de kraftige nye typene, skiller Temporal.Duration seg ut som den definitive løsningen for håndtering av tidsintervaller. Denne artikkelen vil gi deg en grundig innføring i Temporal.Duration, utforske dens evner for aritmetikk, sammenligning og intelligent formatering, og sikre at applikasjonene dine kan håndtere tid med global presisjon og klarhet.
Enten du bygger et globalt logistikksystem, en finansiell handelsplattform, eller en arrangementsplanlegger for flere tidssoner, er forståelsen av Temporal.Duration avgjørende for å eliminere tidsrelaterte tvetydigheter og levere pålitelige, internasjonaliserte brukeropplevelser.
Manglene ved JavaScripts Date-objekt for tidsintervaller
Før vi feirer ankomsten av Temporal.Duration, er det viktig å forstå begrensningene til det eksisterende Date-objektet, spesielt når det gjelder tidsintervaller. Date-objektet representerer et spesifikt tidspunkt, målt i millisekunder siden Unix-epoken (1. januar 1970, UTC). Selv om det kan brukes til å utføre grunnleggende aritmetikk, har det flere iboende feil som gjør det uegnet for robust varighetshåndtering:
-
Endringsbarhet:
Date-objekter er endringsbare (mutable). Enhver operasjon på etDate-objekt endrer dets interne tilstand, noe som kan føre til uventede bivirkninger og vanskelige å spore feil, spesielt i komplekse applikasjoner eller samtidige miljøer.const d = new Date('2023-01-15T10:00:00Z'); const d2 = d; // d2 refererer nå til det samme objektet som d d.setHours(d.getHours() + 1); console.log(d.toISOString()); // 2023-01-15T11:00:00.000Z console.log(d2.toISOString()); // 2023-01-15T11:00:00.000Z (d2 ble også endret!) -
Mangel på et varighetskonsept:
Date-objektet har ingen direkte forestilling om en "varighet" eller "periode". Å beregne forskjellen mellom to datoer resulterer i et antall millisekunder, som deretter må konverteres manuelt til år, måneder, dager, etc. Denne manuelle konverteringen er utsatt for feil, spesielt når man håndterer måneder med variabel lengde eller skuddår.const date1 = new Date('2023-01-01T00:00:00Z'); const date2 = new Date('2023-03-01T00:00:00Z'); const diffMs = date2.getTime() - date1.getTime(); // Hvor mange måneder er dette? Hva med skuddår? // (diffMs / (1000 * 60 * 60 * 24 * 30)) er i beste fall en tilnærming. console.log(`Forskjell i millisekunder: ${diffMs}`); console.log(`Omtrentlige dager: ${diffMs / (1000 * 60 * 60 * 24)}`); // Fungerer for dager, men er ikke robust for måneder/år -
Tidssonetvetydighet:
Date-objekter blander ofte lokal tid og UTC. Selv om de internt lagrer UTC-millisekunder, opererer metodene deres ofte på systemets lokale tidssone som standard, noe som fører til forvirring og inkonsistens når man jobber med distribuerte systemer eller internasjonale brukere. - Utfordringer med sommertid (DST): Overganger til sommertid kan føre til at dager varer 23 eller 25 timer. Enkel aritmetikk (f.eks. å legge til 24 timer til en dato) vil kanskje ikke alltid resultere i neste kalenderdag, noe som fører til feilberegninger når en "dag" antas å være en fast 24-timers periode.
Disse begrensningene har historisk tvunget utviklere til å stole på tredjepartsbiblioteker som Moment.js eller date-fns, eller å skrive kompleks, feilutsatt tilpasset kode for å håndtere tidsintervaller korrekt. Temporal har som mål å bringe disse funksjonene til JavaScript som en innebygd del av språket.
Introduksjon til JavaScript Temporal: En moderne tilnærming til tid
Temporal API er et omfattende, nytt globalt objekt i JavaScript designet for å være en moderne erstatning for det eldre Date-objektet. Dets kjerneprinsipper er uforanderlighet, eksplisitt håndtering av tidssoner og en klar separasjon av ansvarsområder mellom forskjellige tidskonsepter. Temporal introduserer flere nye klasser, som hver representerer et distinkt aspekt av tid:
Temporal.Instant: Et spesifikt, utvetydig tidspunkt, uavhengig av kalender eller tidssone (ligner på et Unix-tidsstempel, men med nanosekundpresisjon).Temporal.ZonedDateTime: Et spesifikt tidspunkt i en bestemt kalender og tidssone. Dette er den mest komplette representasjonen av en spesifikk dato og tid for en bruker.Temporal.PlainDate: En kalenderdato (år, måned, dag) uten tid eller tidssone.Temporal.PlainTime: En klokkeslett (time, minutt, sekund, osv.) uten en dato eller tidssone.Temporal.PlainDateTime: En kalenderdato og klokkeslett sammen, uten en tidssone.Temporal.PlainYearMonth: Et spesifikt år og måned i et kalendersystem.Temporal.PlainMonthDay: En spesifikk måned og dag i et kalendersystem.Temporal.Duration: En signert tidslengde, som "5 timer og 30 minutter" eller "2 dager". Dette er vårt fokus i denne guiden.
Alle Temporal-objekter er uforanderlige (immutable), noe som betyr at operasjoner som å legge til eller trekke fra tid oppretter nye objekter i stedet for å endre eksisterende. Dette forbedrer forutsigbarheten og reduserer feil.
Forståelse av Temporal.Duration
En Temporal.Duration representerer en tidslengde. Viktigere er at den er uavhengig av et spesifikt start- eller sluttpunkt. Det er rett og slett "hvor mye tid" som har gått eller vil gå. Den kan bestå av år, måneder, uker, dager, timer, minutter, sekunder, millisekunder, mikrosekunder og nanosekunder. Hver komponent er et heltall, og kan være positiv eller negativ.
For eksempel, "2 timer og 30 minutter" er en varighet. "Perioden fra 1. januar til 1. mars" er en varighet mellom to spesifikke punkter, som kan *representeres* av en Temporal.Duration, men selve Duration-objektet er bare intervallet.
Opprette en varighet (Duration)
Det finnes flere enkle måter å opprette Temporal.Duration-objekter på:
1. Bruke konstruktøren
Konstruktøren lar deg spesifisere hver komponent direkte. Merk at argumentene er ordnet fra største enhet (år) til minste (nanosekunder).
// new Temporal.Duration(år, måneder, uker, dager, timer, minutter, sekunder, millisekunder, mikrosekunder, nanosekunder)
// En varighet på 2 timer og 30 minutter
const duration1 = new Temporal.Duration(0, 0, 0, 0, 2, 30, 0, 0, 0, 0);
console.log(duration1.toString()); // P2H30M
// En varighet på 1 år, 2 måneder, 3 dager
const duration2 = new Temporal.Duration(1, 2, 0, 3);
console.log(duration2.toString()); // P1Y2M3D
// En varighet på -5 dager
const duration3 = new Temporal.Duration(0, 0, 0, -5);
console.log(duration3.toString()); // P-5D
2. Bruke Temporal.Duration.from() med et objekt
Dette er ofte den mest lesbare måten å opprette varigheter på, da den lar deg spesifisere kun de komponentene du trenger.
// Varighet på 1,5 timer
const halfHourDuration = Temporal.Duration.from({ hours: 1, minutes: 30 });
console.log(halfHourDuration.toString()); // P1H30M
// Varighet på 7 dager (1 uke)
const oneWeekDuration = Temporal.Duration.from({ days: 7 });
console.log(oneWeekDuration.toString()); // P7D
// Varighet med brøkdelssekunder (f.eks. 2,5 sekunder)
const twoPointFiveSeconds = Temporal.Duration.from({ seconds: 2, milliseconds: 500 });
console.log(twoPointFiveSeconds.toString()); // PT2.5S
// Negativ varighet
const negativeDuration = Temporal.Duration.from({ minutes: -45 });
console.log(negativeDuration.toString()); // PT-45M
3. Bruke Temporal.Duration.from() med en ISO 8601-streng
Temporal benytter ISO 8601-varighetsformatet, som er en standard for å representere varigheter. Dette er utmerket for å parse varigheter fra eksterne datakilder.
Formatet ser generelt ut som P[år]Y[måneder]M[uker]W[dager]DT[timer]H[minutter]M[sekunder]S. T skiller datokomponenter fra tidskomponenter.
// 1 år, 2 måneder, 3 dager
const isoDuration1 = Temporal.Duration.from('P1Y2M3D');
console.log(isoDuration1.toString()); // P1Y2M3D
// 4 timer, 5 minutter, 6 sekunder
const isoDuration2 = Temporal.Duration.from('PT4H5M6S');
console.log(isoDuration2.toString()); // PT4H5M6S
// En kombinert varighet
const isoDuration3 = Temporal.Duration.from('P7DT12H30M');
console.log(isoDuration3.toString()); // P7DT12H30M
// Brøkdelssekunder støttes også
const isoDuration4 = Temporal.Duration.from('PT1.5S');
console.log(isoDuration4.toString()); // PT1.5S
Utføre aritmetikk med varigheter
Den sanne styrken til Temporal.Duration ligger i dens aritmetiske evner. Du kan legge til, trekke fra, multiplisere og dele varigheter, og også legge dem til/trekke dem fra andre Temporal dato-/tidstyper. Alle operasjoner returnerer nye Temporal.Duration-objekter på grunn av uforanderlighet.
Legge til varigheter
add()-metoden kombinerer to varigheter.
const sprintDuration = Temporal.Duration.from({ weeks: 2 });
const bufferDuration = Temporal.Duration.from({ days: 3 });
const totalProjectTime = sprintDuration.add(bufferDuration);
console.log(totalProjectTime.toString()); // P2W3D (eller P17D hvis normalisert senere)
// Å legge til en negativ varighet er ekvivalent med subtraksjon
const result = Temporal.Duration.from({ hours: 5 }).add({ hours: -2 });
console.log(result.toString()); // PT3H
Du kan også legge en varighet til et hvilket som helst Temporal dato-/tidsobjekt. Det er her magien skjer, ettersom Temporal håndterer tidssoneendringer og sommertidsoverganger korrekt når det er relevant.
const projectStart = Temporal.PlainDateTime.from('2023-10-26T09:00:00');
const projectDuration = Temporal.Duration.from({ days: 10, hours: 4 });
const projectEnd = projectStart.add(projectDuration);
console.log(projectEnd.toString()); // 2023-11-05T13:00:00
// Med en ZonedDateTime blir tidssonereglene brukt korrekt
const meetingStartUTC = Temporal.ZonedDateTime.from('2024-03-09T14:00:00[UTC]');
const meetingDuration = Temporal.Duration.from({ hours: 1, minutes: 45 });
const meetingEndUTC = meetingStartUTC.add(meetingDuration);
console.log(meetingEndUTC.toString()); // 2024-03-09T15:45:00+00:00[UTC]
// Eksempel som krysser en sommertidsgrense (antar at 'Europe/Berlin' bytter kl. 03:00 den 31. mars 2024)
const springForwardStart = Temporal.ZonedDateTime.from('2024-03-30T22:00:00[Europe/Berlin]');
const twentyFourHours = Temporal.Duration.from({ hours: 24 });
const nextDay = springForwardStart.add(twentyFourHours); // Legger til 24 faktiske timer
console.log(springForwardStart.toString()); // 2024-03-30T22:00:00+01:00[Europe/Berlin]
console.log(nextDay.toString()); // 2024-03-31T23:00:00+02:00[Europe/Berlin] (Lokal tid hoppet over en time)
Legg merke til hvordan å legge til 24 timer til 2024-03-30T22:00:00 i Berlin (som er UTC+1) resulterer i 2024-03-31T23:00:00 (nå UTC+2). Klokken hoppet frem en time, så klokkeslettet er en time senere på samme dato i forhold til starttidspunktet. Dette demonstrerer presist Temporals bevissthet om tidssoner og sommertid når aritmetikk utføres på `ZonedDateTime`.
Trekke fra varigheter
subtract()-metoden fungerer på samme måte som add(), men den fjerner tid.
const deadlineDuration = Temporal.Duration.from({ days: 30 });
const gracePeriod = Temporal.Duration.from({ days: 5 });
const effectiveDeadline = deadlineDuration.subtract(gracePeriod);
console.log(effectiveDeadline.toString()); // P25D
const taskEnd = Temporal.PlainDateTime.from('2023-12-01T17:00:00');
const taskDuration = Temporal.Duration.from({ hours: 8, minutes: 30 });
const taskStart = taskEnd.subtract(taskDuration);
console.log(taskStart.toString()); // 2023-12-01T08:30:00
Multiplisere og dele varigheter
Metodene multiply() og divide() skalerer komponentene i en varighet med en gitt faktor. Dette er nyttig for scenarioer som å beregne total tid for flere iterasjoner av en oppgave.
const trainingSession = Temporal.Duration.from({ minutes: 45 });
const weeklyTraining = trainingSession.multiply(5); // Fem økter i uken
console.log(weeklyTraining.toString()); // PT225M
const totalProjectHours = Temporal.Duration.from({ hours: 160 });
const teamMembers = 4;
const hoursPerMember = totalProjectHours.divide(teamMembers);
console.log(hoursPerMember.toString()); // PT40H
Negere varigheter
negate()-metoden reverserer fortegnet til alle komponentene i en varighet. En positiv varighet blir negativ, og omvendt.
const delayDuration = Temporal.Duration.from({ hours: 3 });
const advanceDuration = delayDuration.negate();
console.log(delayDuration.toString()); // PT3H
console.log(advanceDuration.toString()); // PT-3H
Absoluttverdi av varigheter
abs()-metoden returnerer en ny Temporal.Duration der alle komponentene er gjort positive, noe som effektivt gir deg størrelsen på varigheten uavhengig av fortegn.
const negativeDelay = Temporal.Duration.from({ minutes: -60 });
const positiveDuration = negativeDelay.abs();
console.log(negativeDelay.toString()); // PT-60M
console.log(positiveDuration.toString()); // PT60M
Sammenligne og normalisere varigheter
Å sammenligne varigheter kan være vanskelig, spesielt når ulike enheter er involvert (f.eks. er 1 måned lik 30 dager?). Temporal tilbyr verktøy for både sammenligning og normalisering for å håndtere disse kompleksitetene.
Sammenligne varigheter med compare()
Den statiske metoden Temporal.Duration.compare(duration1, duration2, options) returnerer:
-1hvisduration1er mindre ennduration20hvisduration1er likduration21hvisduration1er større ennduration2
Avgjørende er at når du sammenligner varigheter som inkluderer enheter med variabel lengde som år, måneder eller uker, må du ofte oppgi et relativeTo-alternativ. Denne parameteren er et `Temporal.ZonedDateTime` eller `Temporal.PlainDateTime`-objekt som gir kontekst for hvordan disse enhetene skal tolkes (f.eks. hvor mange dager det er i en bestemt måned eller et bestemt år).
const oneHour = Temporal.Duration.from({ hours: 1 });
const sixtyMinutes = Temporal.Duration.from({ minutes: 60 });
console.log(Temporal.Duration.compare(oneHour, sixtyMinutes)); // 0 (De er ekvivalente)
const oneMonth = Temporal.Duration.from({ months: 1 });
const thirtyDays = Temporal.Duration.from({ days: 30 });
// Uten relativeTo er sammenligninger av måneder/år vanskelige
console.log(Temporal.Duration.compare(oneMonth, thirtyDays)); // 0 (Temporal gjør en rimelig gjetning uten kontekst, ofte basert på gjennomsnitt)
// Med relativeTo er sammenligningen presis basert på kontekstens kalender
const startOfJanuary = Temporal.PlainDate.from('2023-01-01');
const endOfFebruaryLeap = Temporal.PlainDate.from('2024-02-01'); // Skuddår
// I januar 2023 er 1 måned 31 dager
const comparisonJan = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: startOfJanuary });
console.log(`1 måned vs 30 dager i jan 2023: ${comparisonJan}`); // 1 (1 måned > 30 dager)
// I februar 2024 (skuddår) er 1 måned 29 dager
const comparisonFeb = Temporal.Duration.compare(oneMonth, thirtyDays, { relativeTo: endOfFebruaryLeap });
console.log(`1 måned vs 30 dager i feb 2024: ${comparisonFeb}`); // -1 (1 måned < 30 dager)
Normalisere varigheter med normalize() og round()
Varigheter kan representeres på mange måter (f.eks. 90 minutter eller 1 time og 30 minutter). Normalisering og avrunding hjelper til med å standardisere disse representasjonene for konsistens og visning.
normalize()
normalize()-metoden forenkler varighetskomponenter der det er mulig (f.eks. 60 minutter blir 1 time, 24 timer blir 1 dag, forutsatt at `relativeTo`-konteksten tillater det hvis måneder/år er involvert).
const longMinutes = Temporal.Duration.from({ minutes: 90 });
console.log(longMinutes.toString()); // PT90M
console.log(longMinutes.normalize().toString()); // PT1H30M
const multipleDays = Temporal.Duration.from({ hours: 48 });
console.log(multipleDays.toString()); // PT48H
console.log(multipleDays.normalize().toString()); // P2D
round()
round()-metoden er kraftigere for å transformere og avrunde varigheter til spesifikke enheter. Den tar et alternativobjekt med:
largestUnit: Den største enheten som skal inkluderes i resultatet (f.eks. 'years', 'days', 'hours').smallestUnit: Den minste enheten som skal inkluderes i resultatet (f.eks. 'minutes', 'seconds', 'milliseconds').roundingIncrement: Et heltall å avrunde den minste enheten med (f.eks. 5 for å avrunde til nærmeste 5 minutter).roundingMode: Hvordan håndtere uavgjort (f.eks. 'halfExpand', 'trunc', 'ceil', 'floor').relativeTo: Nødvendig for å avrunde varigheter som inneholder år, måneder eller uker.
const complexDuration = Temporal.Duration.from({ hours: 2, minutes: 45, seconds: 30 });
// Avrund til nærmeste time
const roundedToHours = complexDuration.round({ smallestUnit: 'hour' });
console.log(roundedToHours.toString()); // PT3H
// Avrund til nærmeste 30 minutter, behold timer
const roundedTo30Minutes = complexDuration.round({
largestUnit: 'hour',
smallestUnit: 'minute',
roundingIncrement: 30
});
console.log(roundedTo30Minutes.toString()); // PT3H
const preciseDuration = Temporal.Duration.from({ minutes: 123, seconds: 45 });
// Vis som timer og minutter
const formattedDuration = preciseDuration.round({ largestUnit: 'hour', smallestUnit: 'minute' });
console.log(formattedDuration.toString()); // PT2H4M
// Avrunding med måneder/år krever relativeTo
const longTermDuration = Temporal.Duration.from({ months: 1, days: 10 });
const referenceDate = Temporal.PlainDate.from('2023-01-15');
// Avrund til måneder, relativt til en dato
const roundedToMonths = longTermDuration.round({ largestUnit: 'month', smallestUnit: 'month', relativeTo: referenceDate });
console.log(roundedToMonths.toString()); // P1M
Beregne varigheter mellom Temporal-objekter
En av de vanligste bruksområdene for varigheter er å beregne tidsintervallet mellom to spesifikke tidspunkter. Temporal tilbyr until()- og since()-metoder på sine dato-/tidsobjekter for dette formålet.
until()-metoden
until()-metoden beregner varigheten fra mottakerobjektet til argumentobjektet. Den er inkluderende for startpunktet og eksklusiv for sluttpunktet. Den tar et alternativobjekt likt round() for å spesifisere de ønskede enhetene og avrundingsatferden.
const startDate = Temporal.PlainDate.from('2023-01-01');
const endDate = Temporal.PlainDate.from('2023-03-15');
// Varighet i størst mulige enheter (måneder, deretter dager)
const projectLength = startDate.until(endDate);
console.log(projectLength.toString()); // P2M14D
// Varighet kun i dager
const totalDays = startDate.until(endDate, { largestUnit: 'day' });
console.log(totalDays.toString()); // P73D
// Varighet mellom to spesifikke tidspunkter, med respekt for tidssoner
const meetingStart = Temporal.ZonedDateTime.from('2024-07-20T10:00:00[America/New_York]');
const meetingEnd = Temporal.ZonedDateTime.from('2024-07-20T11:30:00[America/New_York]');
const elapsedMeetingTime = meetingStart.until(meetingEnd, { largestUnit: 'hour', smallestUnit: 'minute' });
console.log(elapsedMeetingTime.toString()); // PT1H30M
// Varighet på tvers av tidssoner (fra NYC til London)
const nyStartTime = Temporal.ZonedDateTime.from('2024-08-01T09:00:00[America/New_York]');
const londonEndTime = Temporal.ZonedDateTime.from('2024-08-01T17:00:00[Europe/London]');
const travelDuration = nyStartTime.until(londonEndTime);
console.log(travelDuration.toString()); // PT13H (Faktisk medgått tid, ikke klokkeslettforskjell)
Det siste eksemplet er spesielt innsiktsfullt. Selv om New York er 5 timer bak London, og klokkeslettene er 9 AM og 5 PM på samme dag, beregner until()-metoden korrekt den faktiske medgåtte tiden på 13 timer. Dette er fordi ZonedDateTime implisitt håndterer tidssoneforskjellen.
since()-metoden
since()-metoden er det motsatte av until(). Den beregner varigheten fra argumentobjektet til mottakerobjektet, noe som resulterer i en negativ varighet hvis argumentet er i fremtiden i forhold til mottakeren.
const currentDateTime = Temporal.ZonedDateTime.from('2024-06-15T12:00:00[Europe/Paris]');
const historicEvent = Temporal.ZonedDateTime.from('2024-01-01T00:00:00[Europe/Paris]');
const timeSinceEvent = currentDateTime.since(historicEvent, { largestUnit: 'month', smallestUnit: 'day' });
console.log(timeSinceEvent.toString()); // P5M14D
const futureDate = Temporal.PlainDate.from('2025-01-01');
const pastDate = Temporal.PlainDate.from('2024-01-01');
const durationFromFuture = pastDate.since(futureDate);
console.log(durationFromFuture.toString()); // P-1Y
Håndtere forskjellige enheter og avrunding for beregnede varigheter
Når man beregner varigheter, er det ofte nødvendig å spesifisere `largestUnit` og `smallestUnit` for å få en menneskeleselig representasjon, spesielt for alder, medgått tid eller nedtellinger.
const birthDate = Temporal.PlainDate.from('1990-07-15');
const today = Temporal.PlainDate.from('2024-06-15');
// Beregn alder i år, måneder og dager
const age = birthDate.until(today, { largestUnit: 'year', smallestUnit: 'day' });
console.log(`Alder: ${age.years} år, ${age.months} måneder, ${age.days} dager`); // Alder: 33 år, 11 måneder, 0 dager
// Beregn gjenværende tid for en oppgave i timer og minutter
const now = Temporal.Instant.fromEpochSeconds(Date.now() / 1000);
const deadline = Temporal.Instant.from('2024-07-01T09:00:00Z');
const timeLeft = now.until(deadline, { largestUnit: 'hour', smallestUnit: 'minute', roundingMode: 'ceil' });
console.log(`Tid igjen: ${timeLeft.hours} timer og ${timeLeft.minutes} minutter.`); // Eksempel: Tid igjen: 355 timer og 38 minutter.
Formatere varigheter for et globalt publikum
Selv om Temporal.Duration gir presise, programmatiske representasjoner av tidsintervaller, har den ikke en innebygd toLocaleString()-metode. Dette er med vilje: varigheter er abstrakte tidslengder, og visningen deres kan variere vilt avhengig av kontekst, nasjonale innstillinger og ønsket detaljnivå. Du, som utvikler, er ansvarlig for å presentere varigheter på en brukervennlig og globalt forståelig måte.
ISO 8601-strengrepresentasjon
Standard toString()-metoden til et Temporal.Duration-objekt returnerer dens ISO 8601-strengrepresentasjon. Dette er utmerket for maskin-til-maskin-kommunikasjon, serialisering og lagring, men sjelden for direkte visning til sluttbrukere.
const examDuration = Temporal.Duration.from({ hours: 2, minutes: 15 });
console.log(examDuration.toString()); // PT2H15M
const holidayDuration = Temporal.Duration.from({ weeks: 2, days: 3 });
console.log(holidayDuration.toString()); // P2W3D
Manuell formatering for lesbarhet og internasjonalisering
For brukerrettet visning vil du vanligvis trekke ut komponentene i en varighet og formatere dem ved hjelp av strenginterpolering og JavaScripts Intl API.
Her er et eksempel på en egendefinert funksjon som formaterer en varighet:
function formatDurationToHumanReadable(duration, locale = 'no-NO') {
const parts = [];
// Bruker Intl.NumberFormat for lokalbevisst tallformatering
const numberFormatter = new Intl.NumberFormat(locale);
if (duration.years !== 0) {
parts.push(numberFormatter.format(duration.years) + ' ' + (duration.years === 1 ? 'år' : 'år'));
}
if (duration.months !== 0) {
parts.push(numberFormatter.format(duration.months) + ' ' + (duration.months === 1 ? 'måned' : 'måneder'));
}
if (duration.weeks !== 0) {
parts.push(numberFormatter.format(duration.weeks) + ' ' + (duration.weeks === 1 ? 'uke' : 'uker'));
}
if (duration.days !== 0) {
parts.push(numberFormatter.format(duration.days) + ' ' + (duration.days === 1 ? 'dag' : 'dager'));
}
if (duration.hours !== 0) {
parts.push(numberFormatter.format(duration.hours) + ' ' + (duration.hours === 1 ? 'time' : 'timer'));
}
if (duration.minutes !== 0) {
parts.push(numberFormatter.format(duration.minutes) + ' ' + (duration.minutes === 1 ? 'minutt' : 'minutter'));
}
if (duration.seconds !== 0) {
// Avrund sekunder for visning hvis de har brøkdeler
const roundedSeconds = numberFormatter.format(duration.seconds.toFixed(0)); // Eller toFixed(1) for en desimal
parts.push(roundedSeconds + ' ' + (duration.seconds === 1 ? 'sekund' : 'sekunder'));
}
if (parts.length === 0) {
// Håndter tilfeller der varigheten er null eller veldig liten (f.eks. kun nanosekunder)
if (duration.milliseconds !== 0 || duration.microseconds !== 0 || duration.nanoseconds !== 0) {
const totalMs = duration.milliseconds + duration.microseconds / 1000 + duration.nanoseconds / 1_000_000;
return numberFormatter.format(totalMs.toFixed(2)) + ' millisekunder';
}
return '0 sekunder';
}
// Sett sammen delene med komma og 'og' for den siste delen (grunnleggende norsk sammenføyning)
if (parts.length > 1) {
const lastPart = parts.pop();
return parts.join(', ') + ' og ' + lastPart;
} else {
return parts[0];
}
}
const tripDuration = Temporal.Duration.from({ days: 3, hours: 10, minutes: 45 });
console.log(formatDurationToHumanReadable(tripDuration, 'no-NO')); // 3 dager, 10 timer og 45 minutter
console.log(formatDurationToHumanReadable(tripDuration, 'en-US')); // 3 days, 10 hours and 45 minutes (example of Intl.NumberFormat)
const meetingReminder = Temporal.Duration.from({ minutes: 5 });
console.log(formatDurationToHumanReadable(meetingReminder, 'no-NO')); // 5 minutter
const microDuration = Temporal.Duration.from({ nanoseconds: 1234567 });
console.log(formatDurationToHumanReadable(microDuration, 'no-NO')); // 1,23 millisekunder
For mer avansert flertallsbøyning og lokalisert listeformatering, kan du kombinere dette med Intl.RelativeTimeFormat eller mer komplekse strengmalbiblioteker. Nøkkelen er å skille varighetsberegningen (håndtert av Temporal) fra presentasjonen (håndtert av din formateringslogikk og Intl).
Bruke Intl.DurationFormat (Fremtid/Forslag)
Det pågår et TC39-forslag for Intl.DurationFormat, som har som mål å tilby en innebygd, lokalbevisst måte å formatere varigheter på. Hvis det blir standardisert og implementert, vil det i stor grad forenkle den manuelle formateringen vist ovenfor, og tilby en robust løsning for internasjonalisering direkte i JavaScript-økosystemet.
Det vil sannsynligvis fungere på samme måte som andre Intl-objekter:
// Dette er hypotetisk og basert på forslagets nåværende tilstand
// Sjekk nettleserkompatibilitet før bruk i produksjon
/*
const duration = Temporal.Duration.from({ days: 1, hours: 2, minutes: 30 });
const formatter = new Intl.DurationFormat('no-NO', {
style: 'long',
years: 'long',
days: 'long',
hours: 'long',
minutes: 'long',
});
console.log(formatter.format(duration)); // "1 dag, 2 timer, 30 minutter"
const shortFormatter = new Intl.DurationFormat('fr-FR', { style: 'short', hours: 'numeric', minutes: 'numeric' });
console.log(shortFormatter.format(duration)); // "1 j, 2 h, 30 min"
*/
Selv om det ennå ikke er tilgjengelig i alle miljøer, bør du holde øye med dette forslaget, da det lover å bli den definitive løsningen for global varighetsformatering i fremtiden.
Praktiske brukstilfeller og globale hensyn
Temporal.Duration er ikke bare en akademisk forbedring; den løser virkelige problemer i applikasjoner som opererer på tvers av forskjellige tidssoner og kulturer.
1. Planlegging og arrangementshåndtering
For plattformer som håndterer internasjonale arrangementer, konferanser eller avtaler, er det avgjørende å beregne varigheten av arrangementer, tiden frem til et arrangement, eller hvor lenge en pause varer. Temporal.Duration sikrer at disse beregningene er nøyaktige uavhengig av brukerens plassering eller lokale tidssoneregler.
// Beregn gjenværende tid for et globalt webinar
const now = Temporal.ZonedDateTime.from('2024-07-25T10:00:00[Europe/London]'); // Eksempel på nåværende tid
const webinarStart = Temporal.ZonedDateTime.from('2024-07-26T14:30:00[Asia/Tokyo]');
const timeUntilWebinar = now.until(webinarStart, {
largestUnit: 'hour',
smallestUnit: 'minute',
roundingMode: 'ceil' // Rund opp for å sikre at brukere ikke går glipp av det
});
console.log(`Webinaret starter om ${timeUntilWebinar.hours} timer og ${timeUntilWebinar.minutes} minutter.`);
// Utdata vil være nøyaktig basert på faktisk medgått tid mellom disse to ZonedDateTime-objektene.
2. Ytelsesmålinger og logging
Å måle utførelsestiden for operasjoner, API-responstider eller varigheten av batchjobber krever høy presisjon. Temporal.Duration, spesielt i kombinasjon med Temporal.Instant (som tilbyr nanosekundpresisjon), er ideell for dette.
const startTime = Temporal.Instant.now();
// Simuler en kompleks operasjon
for (let i = 0; i < 1_000_000; i++) { Math.sqrt(i); }
const endTime = Temporal.Instant.now();
const executionDuration = startTime.until(endTime);
// Formater til sekunder med millisekunder for visning
const formattedExecution = executionDuration.round({ smallestUnit: 'millisecond', largestUnit: 'second' });
console.log(`Operasjonen tok ${formattedExecution.seconds}.${String(formattedExecution.milliseconds).padStart(3, '0')} sekunder.`);
3. Finansielle beregninger
I finans er presise tidsintervaller avgjørende for å beregne renter, lånevilkår eller investeringsperioder. Det nøyaktige antallet dager, måneder eller år i en periode må være nøyaktig, spesielt når man har med skuddår eller spesifikke månedslengder å gjøre.
const loanStartDate = Temporal.PlainDate.from('2023-04-01');
const loanEndDate = Temporal.PlainDate.from('2028-03-31');
const loanTerm = loanStartDate.until(loanEndDate, { largestUnit: 'year', smallestUnit: 'month' });
console.log(`Låneperiode: ${loanTerm.years} år og ${loanTerm.months} måneder.`); // 4 år og 11 måneder
4. Internasjonaliseringsutfordringer på nytt
-
Tidssoner og sommertid:
Temporal.Durationer i seg selv tidssoneagnostisk; den representerer en fast tidslengde. Men når du legger til eller trekker fra enDurationtil/fra enZonedDateTime, anvender Temporal tidssonereglene korrekt, inkludert sommertidsoverganger. Dette sikrer at en "24-timers varighet" virkelig flytter enZonedDateTime24 faktiske timer frem, selv om det betyr å krysse en sommertidsgrense som gjør *klokkeslettdagen* kortere eller lengre. Denne konsistensen er en stor fordel over `Date`. -
Kulturelle variasjoner: Forskjellige kulturer uttrykker varigheter forskjellig. Mens
Temporal.Durationgir de rå komponentene (år, måneder, dager, osv.), er det ditt ansvar å bruke `Intl`-API-er eller egendefinert logikk for å presentere disse på en måte som gir gjenklang hos brukerens nasjonale innstillinger. For eksempel kan noen kulturer uttrykke "1 time og 30 minutter" som "90 minutter" eller bruke andre flertallsregler. -
Kalendersystemer: Temporal støtter også forskjellige kalendersystemer (f.eks. japansk, persisk, islamsk kalender). Selv om
Durationi seg selv er kalenderagnostisk, respekterer aritmetikken den spesifikke kalenderens regler (f.eks. antall dager i en måned, skuddårsregler innenfor den kalenderen) når den interagerer med kalenderbevisste typer somPlainDateellerZonedDateTime. Dette er avgjørende for globale applikasjoner som kan trenge å vise datoer i ikke-gregorianske kalendere.
Temporals nåværende status og adopsjon
Per sent 2023/tidlig 2024 er JavaScript Temporal API et Stage 3 TC39-forslag. Dette betyr at spesifikasjonen i stor grad er stabil, og den gjennomgår implementering og testing i ulike JavaScript-motorer og nettlesere. Store nettlesere som Chrome, Firefox og Safari jobber aktivt med å implementere Temporal, med eksperimentelle flagg eller tidlige versjoner allerede tilgjengelige.
Selv om det ennå ikke er universelt tilgjengelig uten en polyfill, indikerer dets avanserte stadium at det er svært sannsynlig at det blir en standard del av JavaScript. Utviklere kan begynne å eksperimentere med Temporal i dag ved å bruke polyfills eller ved å aktivere eksperimentelle funksjoner i sine nettleserutviklingsmiljøer. Tidlig adopsjon lar deg komme i forkant, forstå fordelene og integrere det i prosjektene dine etter hvert som nettleserstøtten rulles ut.
Den eventuelle utbredte adopsjonen vil betydelig redusere behovet for eksterne dato-/tidsbiblioteker, og gi en robust, innebygd løsning for tidsstyring i JavaScript.
Beste praksis for bruk av Temporal Duration
For å maksimere fordelene med Temporal.Duration i applikasjonene dine, bør du vurdere disse beste praksisene:
-
Foretrekk
Temporal.Duration.from(): Når du oppretter varigheter fra kjente komponenter eller ISO-strenger, erTemporal.Duration.from()med et objektliteral ofte mer lesbart og mindre feilutsatt enn konstruktørens posisjonsargumenter. -
Bruk
relativeTofor tvetydige sammenligninger: Oppgi alltid etrelativeTo-alternativ når du sammenligner eller avrunder varigheter som inneholder år, måneder eller uker. Dette sikrer nøyaktige beregninger ved å gi den nødvendige kalenderkonteksten. -
Utnytt
until()ogsince(): For å beregne intervallet mellom to spesifikke tidspunkter, foretrekkuntil()- ogsince()-metodene på Temporal dato-/tidsobjekter. De håndterer kompleksiteten med tidssoner og sommertid korrekt. -
Normaliser og avrund for visning: Før du presenterer varigheter for brukere, bør du vurdere å bruke
normalize()og spesieltround()for å konvertere varigheten til de mest passende og forståelige enhetene (f.eks. konvertere 90 minutter til "1 time og 30 minutter"). -
Skill intern representasjon fra visning: Hold de interne varighetsberegningene presise med
Temporal.Duration. Transformer og formater kun for visning når du gjengir brukergrensesnittet, ved hjelp av egendefinerte formateringsfunksjoner ogIntl-API-et for global nøyaktighet. - Hold deg oppdatert: Følg med på fremdriften til Temporal API og nettleserkompatibilitet. Etter hvert som det beveger seg mot full standardisering, vil økosystemet utvikle seg, og nye verktøy eller beste praksis kan dukke opp.
- Vær bevisst på presisjon: Selv om Temporal støtter nanosekundpresisjon, bruk kun den presisjonen du virkelig trenger. Høyere presisjon kan noen ganger gjøre feilsøking vanskeligere eller resultere i uventet avrunding ved konvertering til visninger med lavere presisjon.
Konklusjon
Introduksjonen av Temporal.Duration markerer et betydelig fremskritt for JavaScript-utviklere som sliter med tidsintervallaritmetikk. Ved å tilby et uforanderlig, eksplisitt og globalt bevisst API, adresserer Temporal de langvarige begrensningene til det eldre Date-objektet.
Du kan nå trygt utføre komplekse tidsberegninger, måle perioder nøyaktig og presentere varigheter på en måte som er både presis og kulturelt passende for brukere over hele verden. Enten du beregner varigheten av en programvareutgivelsessyklus, gjenværende tid til en global produktlansering, eller den nøyaktige alderen til en person, tilbyr Temporal.Duration verktøyene du trenger for å bygge robuste og pålitelige applikasjoner.
Omfavn Temporal.Duration og transformer hvordan du håndterer tid i JavaScript-prosjektene dine. Fremtiden for dato- og tidshåndtering i JavaScript er her, og den lover en mer forutsigbar, kraftig og globalt kompatibel utviklingsopplevelse.